/***
 * Copyright (c) 2021-2031 murenchao
 * fig is licensed under Mulan PubL v2.
 * You can use this software according to the terms and conditions of the Mulan PubL v2.
 * You may obtain a copy of Mulan PubL v2 at:
 *       http://license.coscl.org.cn/MulanPubL-2.0
 * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
 * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
 * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
 * See the Mulan PubL v2 for more details.
 */
package cool.taomu.software.fig.asm.aspect2

import cool.taomu.software.fig.asm.aspect2.annotation.Around
import cool.taomu.software.fig.asm.aspect2.annotation.Aspect
import cool.taomu.software.fig.asm.generate.GenerateUtils
import cool.taomu.software.fig.asm.utils.BytecodeUtils
import cool.taomu.software.fig.asm.utils.FigGeneratorAdapter
import cool.taomu.software.fig.classloader.FigClassLoaderManage
import cool.taomu.software.fig.objectpool.ObjectPool
import cool.taomu.software.fig.objectpool.ObjectPoolFactory
import java.io.FileOutputStream
import java.util.HashMap
import java.util.UUID
import org.objectweb.asm.Opcodes
import org.objectweb.asm.Type
import org.objectweb.asm.commons.GeneratorAdapter
import org.objectweb.asm.commons.Method
import org.slf4j.Logger
import org.slf4j.LoggerFactory

class AspectUtils {
    val aspectClassMap = new HashMap<String, Class<?>>();
    val static AspectUtils instance = new AspectUtils();
    val static CLASS_NAME_PREFIX = "FigAspect_";
    val isDebug = false;

    private new() {
    }

    def static synchronized getInstance() {
        return instance;
    }

    def build(Class<?> aspectClass) {
        val aspect = aspectClass.getAnnotation(Aspect);
        if (aspect !== null) {
            for (method : aspectClass.declaredMethods) {
                val around = method.getAnnotation(Around);
                if (around !== null) {
                    around.value.forEach [ v |
                        aspectClassMap.put(v.name, aspect(v, aspectClass, method.name));
                    ]
                }
            }
        }
        return this;
    }

    def getClass(Class<?> zlass) {
        return this.getClass(zlass.name);
    }

    def getClass(String name) {
        return aspectClassMap.get(name);
    }

    def aspect(Class<?> dest, Class<?> aspect, String methodName) {
        // 根据dest创建子类 
        var g = new GenerateUtils();
        // 创建一个子类名
        val uuid = CLASS_NAME_PREFIX + UUID.randomUUID.toString().replace("-", "");
        /**
         * 生成子类
         * 
         * 添加一个重写父类中的方法，并创建AspectHandle给 切面类
         */
        var newClassBytes = g.gen(uuid, dest, null, [ v, s, i |
            // 给子类增加日志
            v.visitField(Opcodes.ACC_PRIVATE + Opcodes.ACC_STATIC, "LOG", Type.getType(Logger).getDescriptor(), null,
                null).visitEnd();
            val gar = new GeneratorAdapter(Opcodes.ACC_STATIC, Method.getMethod("void <clinit>()"), null, null, v);
            gar.push(Type.getObjectType(uuid));
            gar.invokeStatic(Type.getType(LoggerFactory),
                Method.getMethod("org/slf4j/Logger getLogger(java/lang/Class)", true))
            gar.putStatic(Type.getObjectType(uuid), "LOG", Type.getType(Logger))
            gar.returnValue
            gar.endMethod;
            // 重写父类方法 
            dest.declaredMethods.forEach [ m |
                val method = Method.getMethod(m);
                val ga = new FigGeneratorAdapter(Opcodes.ACC_PUBLIC, method, null, null, v);
                ga.newInstance("aspect", aspect);
                ga.newInstance("aspectHandle", AspectHandle);
                ga.loadLocal("aspectHandle");
                ga.newArray(method.argumentTypes.size, Object, [ it |
                    BytecodeUtils.buildArguments(method, it);
                ])
                ga.invokeVirtual(AspectHandle, "void setArguments(Object[])");
                ga.loadLocal("aspectHandle");
                ga.push(dest);
                ga.push(method.name);
                ga.newArray(method.argumentTypes.size, Class, [ it |
                    if (method.argumentTypes.size > 0) {
                        for (var index = 0; index < method.argumentTypes.size; index++) {
                            it.dup;
                            it.push(index);
                            it.push(method.argumentTypes.get(index));
                            it.visitInsn(Opcodes.AASTORE);
                        }
                    } else {
                        it.visitInsn(Opcodes.ACONST_NULL);
                    }
                ]);
                ga.invokeVirtual(Class,
                    "java/lang/reflect/Method getDeclaredMethod (java/lang/String,java/lang/Class[])", true);
                ga.invokeVirtual(AspectHandle, "void setMethod(java/lang/reflect/Method)", true);
                // 创建dest的实例
                ga.newInstance("dest", dest);
                ga.loadLocal("aspectHandle");
                ga.loadLocal("dest");
                ga.invokeVirtual(AspectHandle, "void setInstance(Object)");
                ga.loadLocal("aspect");
                ga.loadLocal("aspectHandle");
                var aspectMethodName = aspect.getDeclaredMethod(methodName, AspectHandle);
                ga.invokeVirtual(aspect, aspectMethodName);
                BytecodeUtils.buildReturnValue(m, ga)
                ga.returnValue
                ga.endMethod

            ]
        ], isDebug);
        if (isDebug) {
            try(var fos = new FileOutputStream("./a.class")) {
                fos.write(newClassBytes);
            }
        }
        var fig = new FigClassLoaderManage();
        fig.register(uuid, newClassBytes);
        return fig.getClassLoader(uuid);
    }
}
